__author__ = 'Muscle'

import weakref
from mui.muicomponent import MUIComponent



class MUIArrowController:
    """ArrowController is a base class to support arrow key control of focused component.
    """
    arrowkeys = ['UP', 'DOWN', 'LEFT', 'RIGHT']
    keymap = {
        'u':'UP',
        'd':'DOWN',
        'l':'LEFT',
        'r':'RIGHT',
        'up':'UP',
        'down':'DOWN',
        'left':'LEFT',
        'right':'RIGHT',
        'UP':'UP',
        'DOWN':'DOWN',
        'LEFT':'LEFT',
        'RIGHT':'RIGHT'
    }
    def __init__(self):
        assert isinstance(self, MUIComponent)
        self.arrow_maps = {}
        self.bind('Close', self.clear, noarg = True)

    def clear(self):
        self.arrow_maps = {}

    def dirmap(self, comp, direction):
        """Set direction map for component.

        @parem direction = [{[:keymap:]:[control, None], ...}, {}]
               None deletes the old mapping on that direction;
               {} deletes the whole mapping.
               ex: direction = {u: fileBrowser, l: mainMenu, r: btnDown}
                   direction = {right: None}
                   direction = {}
        """
        ref = weakref.ref(comp)
        if direction == {}:
            if ref in self.arrow_maps:
                del self.arrow_maps[ref]
            return

        if not ref in self.arrow_maps:
            self.arrow_maps[ref] = {}

        arrowmap = self.arrow_maps[ref]
        for d in direction:
            dirkey = ArrowController.keymap[d]
            control = direction[d]
            if control is None: # delete the old mapping if it exists
                if dirkey in arrowmap:
                    del arrowmap[dirkey]
            else:
                arrowmap[dirkey] = weakref.ref(control)
        if arrowmap == {}:
            del self.arrow_maps[ref]

    def direct(self, msgType, dirkey, modifiers):
        """Set focus using direction map on dirkey.

        Skip disabled or invisible children
        """
        if not self.focus:
            return False
        if dirkey not in MUIArrowController.arrowkeys:
            return False

        focus = self
        while focus.focusChild:
            focus = focus.focusChild

        visited = []
        while focus != self.parent:
            next = focus
            nextref = weakref.ref(next)
            visited.append(next)

            while nextref in self.arrow_maps:
                arrowmap = self.arrow_maps[nextref]
                if dirkey not in arrowmap:
                    break

                nextref = arrowmap[dirkey]
                next = nextref()
                if not next or next in visited: # recursive circle
                    break

                if next.enabled and next.isVisible():
                    next.setFocus(hint=dirkey, force=True)
                    return True
                else:
                    visited.append(next)

            focus = focus.parent
        return False

